package com.vf.cloud.rendering.api.service.impl;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.sql.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.springframework.stereotype.Service;

import com.vf.cloud.rendering.api.service.IDownloadClient;
import com.vf.cloud.rendering.common.constant.Cache;

import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class DownloadClientImpl implements IDownloadClient {

	private final static long per_page = 1024l * 1024l * 50l;
	ExecutorService pool = Executors.newFixedThreadPool(100);

	@Override
	public void download(String sceneId) {

		String down_path = Cache.local.getProjectPath();
		String folder = DateUtil.format(new Date(System.currentTimeMillis()), "yyyMMddHHmmss");
		File f = new File(String.format("%s\\%s", down_path, folder));
		if (!f.exists()) {
			f.mkdirs();
		}

		try {
			FileInfo fileInfo = download(sceneId, 0, 10, -1, null, down_path, folder);
			if (fileInfo != null) {
				long pages = fileInfo.fSize / per_page;
				for (int i = 0; i <= pages; i++) {
					pool.submit(new Downloader(sceneId, i * per_page, (i + 1) * per_page - 1, i,
							fileInfo.fName, down_path, folder));
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 返回文件名 跟大小
	private FileInfo download(String sceneId, long start, long end, long page, String fName,
			String down_path, String folder) throws Exception {
		// 断点下载 文件存在不需要下载
		File file = new File(String.format("%s\\%s", down_path, folder), page + "-" + fName);
		// 探测必须放行 若下载分片只下载一半就锻炼需要重新下载所以需要判断文件是否完整
		if (file.exists() && page != -1 && file.length() == per_page) {
			return null;
		}
		// 需要知道 开始-结束 = 分片大小
		HttpClient client = HttpClients.createDefault();
		// httpclient进行请求
		HttpGet httpGet = new HttpGet(String.format("http://%s:8880/down", Cache.dispatch.getIp()));

		httpGet.setHeader("Range", "bytes=" + start + "-" + end);
		httpGet.addHeader("terminalId", Cache.local.getMac());
		httpGet.setHeader("sceneId", sceneId);
		httpGet.setHeader("folder", folder);

		HttpResponse response = client.execute(httpGet);
		if (response.getHeaders("message") != null && response.getHeaders("message").length > 0) {
			throw new Exception(URLDecoder.decode(response.getHeaders("message")[0].getValue(), "utf-8"));
		}
		String fSize = response.getFirstHeader("fSize").getValue();
		fName = URLDecoder.decode(response.getFirstHeader("fName").getValue(), "utf-8");
		HttpEntity entity = response.getEntity();// 获取文件流对象
		InputStream is = entity.getContent();
		// 临时存储分片文件
		FileOutputStream fos = new FileOutputStream(file);
		byte[] buffer = new byte[1024];// 定义缓冲区
		int ch;
		while ((ch = is.read(buffer)) != -1) {
			fos.write(buffer, 0, ch);
		}
		is.close();
		fos.flush();
		fos.close();
		// 判断是不是最后一个分片
		if (end - Long.valueOf(fSize) > 0) {
			// 合并
			try {
				mergeFile(sceneId, down_path, folder, fName, page);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return new FileInfo(Long.valueOf(fSize), fName);
	}

	class Downloader implements Runnable {
		long start;
		long end;
		long page;
		String fName;
		String down_path;
		String folder;
		String sceneId;

		public Downloader(String sceneId, long start, long end, long page, String fName,
				String down_path, String folder) {
			this.sceneId = sceneId;
			this.start = start;
			this.end = end;
			this.page = page;
			this.fName = fName;
			this.down_path = down_path;
			this.folder = folder;
		}

		@Override
		public void run() {
			try {
				download(sceneId, start, end, page, fName, down_path, folder);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private void mergeFile(String sceneId, String down_path, String folder, String fName, long page)
			throws Exception {
		// 归并文件位置
		File file = new File(String.format("%s\\%s", down_path, folder), fName);
		BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
		for (int i = 0; i <= page; i++) {
			File tempFile = new File(String.format("%s\\%s", down_path, folder), i + "-" + fName);
			// 分片没下载或者没下载完需要等待

			while (!file.exists() || (i != page && tempFile.length() < per_page)) {
				Thread.sleep(100);
			}

			byte[] bytes = FileUtils.readFileToByteArray(tempFile);
			os.write(bytes);
			os.flush();
			tempFile.delete();
		}
		File file1 = new File(String.format("%s\\%s", down_path, folder), -1 + "-null");
		file1.delete();
		os.flush();
		os.close();
		Download_Complete(sceneId);
		unZipFile(String.format("%s\\%s\\%s", down_path, folder, fName), String.format("%s\\%s", down_path, folder),
				sceneId);

	}

	/**
	 * 解压zip文件到指定目录
	 * 
	 * @param inputFile   需要压缩的文件路径
	 * @param destDirPath 指定保存的目录路径
	 * @throws Exception
	 */
	public void unZipFile(String inputFile, String destDirPath, String sceneId) {
		File srcFile = new File(inputFile);// 获取当前压缩文件
		ZipInputStream zIn = null;
		InputStream in=null;
		try {

			log.info(inputFile);
			log.info(destDirPath);
			// 判断源文件是否存在
			if (!srcFile.exists()) {
				throw new Exception(srcFile.getPath() + "所指文件不存在");
			}
			// 开始解压
			
			in =new FileInputStream(srcFile);
			
			// 构建解压输入流
			zIn = new ZipInputStream(in);
			ZipEntry entry = null;
			File file = null;
			while ((entry = zIn.getNextEntry()) != null) {
				if (!entry.isDirectory()) {
					file = new File(destDirPath, entry.getName());
					if (!file.exists()) {
						new File(file.getParent()).mkdirs();// 创建此文件的上级目录
					}
					OutputStream out = new FileOutputStream(file);
					BufferedOutputStream bos = new BufferedOutputStream(out);
					int len = -1;
					byte[] buf = new byte[1024];
					while ((len = zIn.read(buf)) != -1) {
						bos.write(buf, 0, len);
					}
					// 关流顺序，先打开的后关闭
					bos.close();
					out.close();
				}
			}
			
			Decompression_Completed(sceneId, inputFile);
		} catch (Exception e) {
			log.error(e.getMessage());
		} finally {
			if(zIn!=null) {
				try {
					
					System.out.println(">>>>>>>>>>>>>>>>>>>close");
					
					zIn.closeEntry();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(in!=null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+srcFile.delete());
		}
	}

	/**
	 * 下载完成
	 */
	private void Download_Complete(String sceneId) {
		HttpClient client = HttpClients.createDefault();
		HttpGet httpGet = new HttpGet(String.format("http://%s:8880/down", Cache.dispatch.getIp()));
		httpGet.addHeader("terminalId", Cache.local.getMac());
		httpGet.setHeader("sceneId", sceneId);
		httpGet.setHeader("complete", "Download_Complete");

		try {
			client.execute(httpGet);
		} catch (ClientProtocolException e) {
		} catch (Exception e) {
			e.printStackTrace();
			log.info(e.getMessage());
		}
	}

	private void Decompression_Completed(String sceneId, String inputFile) {
		HttpClient client = HttpClients.createDefault();
		HttpGet httpGet = new HttpGet(String.format("http://%s:8880/down", Cache.dispatch.getIp()));
		httpGet.addHeader("terminalId", Cache.local.getMac());
		httpGet.setHeader("sceneId", sceneId);
		httpGet.setHeader("complete", "Decompression_Completed");

		try {
			client.execute(httpGet);
		} catch (ClientProtocolException e) {
		} catch (Exception e) {
			e.printStackTrace();
			log.info(e.getMessage());
		} finally {
		}
	}

	// 使用内部类实现
	class FileInfo {
		long fSize;
		String fName;

		public FileInfo(long fSize, String fName) {
			this.fSize = fSize;
			this.fName = fName;
		}
	}

}
